///////////////////////////////////////////////////////////////////////////////
//File Name			:	"CGame.h"
//////////////////////////////////////////////////////////////////////////////

#include "Game.h"
#include "Event System/EventManager.h"
#include "Debug Console/DebugConsole.h"
#include "Timer/DeltaTimer.hpp"
#include "Event System/Event.h"
#include "Game Objects/ObjectManager.h"
#include "Game Objects/ObjectFactory.h"


//The bigger this number the more cpu the computer will use
//Is using about 50% cpu right now
// DO NOT MAKE THIS 0 or I KILL U!!!!!!!!
#define UINT_LIMIT_UPDATETHREAD 80

//Read Function
unsigned int __stdcall Update(void* that)
{
	CGame* p_game = CGame::GetInstance();
	
	while(true)
	{
		

		if(p_game->GetIsShutDownTime())
			break;

		p_game->EventManager->TransfertoUpdateLocal();
		p_game->EventManager->ProcessEventsUpdate();

		
		p_game->SetUnlockPerformance(p_game->GetUnlockPerformance()+1);
		p_game->m_fUpdateTimer->Update();
		
		p_game->SetUpdateCounter(p_game->m_fUpdateTimer->GetDelta()+p_game->GetUpdateCounter());
		if( p_game->GetUpdateCounter() > 1)
		{
			char buffer[128];
			sprintf_s(buffer,128,"Update Thread: %d\n",p_game->m_fUpdateTimer->GetFPS());

			if(p_game->GetConsole())
				p_game->GetConsole()->Display(buffer);


			p_game->SetUpdateCounter(0.0f);
		}

		unsigned int unSize = p_game->GetVectorStates().size();

		if(	unSize != 0)
			p_game->GetVectorStates()[unSize - 1]->Update(p_game->m_fUpdateTimer->GetDelta());
		

		TBaseEventData* RenderData = new RenderEvent(p_game->GetObjectManager()->CreateListForRender());
		p_game->EventManager->SendEventUpdateCrossToRender(EVENT_RENDER,RenderData);


		p_game->EventManager->TransfertoRenderCentral();

		if(p_game->GetUnlockPerformance()%UINT_LIMIT_UPDATETHREAD == 0)
			Sleep(1);
		
	}
	
	p_game->EventManager->SetClearFillRender(true);
	p_game->EventManager->SetClearFillUpdate(true);

	return 1;
	
};


CGame::CGame()
{
	
}

CGame::~CGame()
{

	LeaveCriticalSection(LockSection);
	
	if(LockSection)
		delete LockSection;

}

//Singleton Accesor
CGame* CGame::GetInstance()
{
	static CGame instance;
	return &instance;
}

void CGame::Initialize(HWND hWnd,HINSTANCE hInstance, int nScreenWidth, int nScreenHeight,bool bIsWindowed)
{

	EventManager = CEventManager::GetInstance();
	EventManager->Initialize();

	EventManager->RegisterForEventInRender(EVENT_NONE,this);

	MemoryManager.Init(1179648);

	//////////////////////////////////////
	//This function launches the thread
	//////////////////////////////////////
	InternalInitialize();

	// Renderer
	m_pRenderer = new CRenderMaster();
	m_pRenderer->Initialize( hWnd, "Root/Resource/Shaders/ShaderMaster.txt" );
	//m_pRenderer->LoadTROGDOR( "Root/Resource/Models/trogdor.mesh" );
}

void CGame::Shutdown()
{

	SetShutDownTime(true);

	if(ThreadHandle != NULL)
		WaitForSingleObject(ThreadHandle,INFINITE);

	CloseHandle(ThreadHandle);


	ClearAllStates();
	EventManager->UnregisterListenerAllRender(this);
	
	LockIt();
		EventManager->ShutdownEventManager();
		if(m_Console)
			m_Console->ShutDown();
	UnLockIt();

	delete m_pRenderer;
	
	m_pObjectFactory->ShutDown();
	m_pObjectManager->Shutdown();
}

bool CGame::Main()
{
	m_fRenderTimer->Update();

	//3 steps
	if(Input()== false)
		return false;
	

	Render();

		static float x = 0;		

		x += m_fRenderTimer->GetDelta();
		if( x > 1)
		{
			char buffer[128];
			sprintf_s(buffer,128,"Render Thread: %d\n",m_fRenderTimer->GetFPS());
			
			if(GetConsole())
				GetConsole()->Display(buffer);
			x =0;
		}

	return true;
}

//input
bool CGame::Input()
{
	unsigned int unSize = GetVectorStates().size();

	if(unSize != 0 && !GetVectorStates()[unSize - 1]->Input())	
		return false;
	
	return true;
}

//Render
void CGame::Render()
{
	EventManager->TransfertoRenderLocal();

	EventManager->ProcessEventsRender();

	// Renderer
	m_pRenderer->Render( NULL );

	EventManager->TransfertoUpdateCentral();
}

void CGame::ChangeState(CBaseGameState* pNewState)
{
	ClearAllStates();
	
	Push(pNewState);
}

void CGame::Push(CBaseGameState* pIGameState)
{
	pIGameState->Enter();
	
	LockIt();
		m_vVectorStates.push_back(pIGameState);
	UnLockIt();
}

void CGame::PushNoEnter(CBaseGameState* pIGameState)
{
	LockIt();
		m_vVectorStates.push_back(pIGameState);
	UnLockIt();
}

void CGame::Pop()
{
	LockIt();
		m_vVectorStates[m_vVectorStates.size()-1]->Exit();
		m_vVectorStates.pop_back();
	UnLockIt();
}

void CGame::ClearAllStates()
{
	LockIt();
		for(unsigned int x =0; x<m_vVectorStates.size();x++)
			m_vVectorStates[x]->Exit();
		m_vVectorStates.clear();
	UnLockIt();
}

void CGame::InternalInitialize()
{
		updateCounter = (float*)MemoryManager.Allocate(sizeof(float));
		unlockPerformance = (unsigned int*)MemoryManager.Allocate(sizeof(unsigned int));

		*updateCounter = 0.0f;
		*unlockPerformance = 0;

		m_fRenderTimer = deltaTimer::GetInstanceRender();
		m_fUpdateTimer = deltaTimer::GetInstanceUpdate();
	
		//#if _DEBUG
			m_Console = CDebugConsole::GetInstance();
		//#else
		//	m_Console = NULL;
		//#endif


		if(m_Console)
			m_Console->Initialize();
		//Object Factory
		m_pObjectFactory = CObjectFactory::GetInstance();

		m_pObjectFactory->Initialize();

		//Object Manager
		m_pObjectManager = CObjectManager::GetInstance();
		
		m_pObjectManager->Initialize();


		m_bTimeToLeave = false;
		
		LockSection = new CRITICAL_SECTION();

		InitializeCriticalSection(LockSection);

		unsigned int ThreadId = 1;
		
		ThreadHandle = (HANDLE)_beginthreadex(NULL,0,&Update,NULL,0,&ThreadId);

}

void CGame::ProcessEvent(CEvent* pEvent)
{
	if(pEvent->GetEventID() == EVENT_NONE)
	{
		m_Console->Display("Testing\n");

		//ALl programmers must delete there reference or u leak,,,, U leak I KILL UUUU!!!!
		pEvent->SubtractRef();	
	}

	


	return;
}